﻿using System.Collections.Generic;

using G.Model;

namespace G.Runtime
{
    public class FightEventArgs
    {
        public GameElement attacker;
        public GameElement defender;
    }
    
    public class StandardGameController : GameController
    {
        public delegate void BeforeEndOfTurnHandler(GameController pGameController);
        public delegate void FightHandler(FightEventArgs args);
        
        public event ElementsReadyToMoveChangedHandler ElementsReadyToMoveChangedEvent;
        public event FightHandler Fight;

        private readonly GameSpaceDef _GameDef;
        private readonly GameSpace _Game;
        private readonly StringGameValue _NameOfPlayerWithTurn;

        private readonly List<GameElement> _ElementsReadyToMove = new List<GameElement>();
        private readonly List<GameElement> _ElementsMoved = new List<GameElement>();
        private readonly List<GamePlayer> _PlayOrder = new List<GamePlayer>();

        private GamePlayer _PlayerWithTurn;
        private int _Index;
        private bool _TurnValue;

        public StandardGameController(GameSpace pGame)
        {
            _GameDef = pGame.Def;
            _Game = pGame;

            {
                GameEventDef def = new GameEventDef(Constants.END_OF_MOVE);
                GameEvent ge = new GameEvent(pGame, def);
                pGame.Globals.Events.Add(ge);
                GameEvent e = pGame.Globals.Events[Constants.END_OF_MOVE];
                e.InvokeEvent += OnEndOfMove;
            }
            {
                GameEventDef def = new GameEventDef(Constants.END_OF_TURN);
                GameEvent ge = new GameEvent(pGame, def);
                pGame.Globals.Events.Add(ge);
                GameEvent e = pGame.Globals.Events[Constants.END_OF_TURN];
                e.InvokeEvent += OnEndOfTurn;
            }

            {
                GameValueDef def = new GameValueDef(Constants.PLAYER, System.TypeCode.String);
                GameValueDefs values = _GameDef.GlobalDef.GameValueDefs;
                values.Add(def);
                _NameOfPlayerWithTurn = new StringGameValue(def);
            }

            pGame.Elements.ElementMovedEvent += this.OnElementMoved;
            pGame.Elements.ElementAddedEvent += this.OnElementAdded;
        }

        public void Start()
        {
            DetermineOrder();
            DeterminePlayersTurn();
            DetermineElementsReadyToMove();
        }

        public void EndGame()
        {
            _Index = 0;
            _PlayerWithTurn = null;
            _NameOfPlayerWithTurn.Value = "";
            _ElementsReadyToMove.Clear();
            _PlayOrder.Clear();
            if (ElementsReadyToMoveChangedEvent != null)
            {
                ElementsReadyToMoveChangedEvent(_ElementsReadyToMove);
            }
        }
        
        public void RemovePlayer(GamePlayer player)
        {
            if (_PlayerWithTurn != null && _PlayerWithTurn.Name == player.Name)
            {
                if (_Index > 0)
                {
                    _Index--;
                }
            }
            _PlayOrder.Remove(player);
        }

        public StringGameValue NameOfPlayerWithTurn
        {
            get { return _NameOfPlayerWithTurn; }
        }

        public bool TurnValue
        {
            get { return _TurnValue; }
        }

        public ICollection<GameElement> ElementsReadyToMove
        {
            get { return _ElementsReadyToMove; }
        }

        public void DetermineOrder()
        {
            _PlayOrder.Clear();

            if (_GameDef.GamePlayerDefs == null)
            {
                return;
            }

            foreach (GamePlayer player in _Game.Players)
            {
                if (!_PlayOrder.Contains(player))
                {
                    _PlayOrder.Add(player);
                }
            }

            _PlayOrder.Sort();
        }

        public void DeterminePlayersTurn()
        {
            _PlayOrder.Sort();
            if (_Index > _PlayOrder.Count - 1)
            {
                _TurnValue = false;
                _Index = 0;
            }

            for (int i = _Index; i < _PlayOrder.Count;)
            {
                GamePlayer player = _PlayOrder[i];

                if (_PlayerWithTurn != player)
                {
                    _PlayerWithTurn = player;
                    _NameOfPlayerWithTurn.Value = player.Name;
                    
                    DetermineElementsReadyToMove();
                    if (_ElementsReadyToMove.Count > 0)
                    {
                        _PlayerWithTurn.FireOnTurn(_ElementsReadyToMove);
                    }
                }
                break;
            }
        }

        private bool IsFirst(GamePlayer player)
        {
            if (player == null)
            {
                return false;
            }
            int index = _PlayOrder.IndexOf(player);
            return (index == 0);
        }

        private bool IsLast(GamePlayer player)
        {
            int index = _PlayOrder.IndexOf(player);
            return (index == _PlayOrder.Count - 1);
        }

        public void DetermineElementsReadyToMove()
        {
            if (_PlayerWithTurn == null)
            {
                return;
            } 

            _ElementsReadyToMove.Clear();
            foreach (GameElement element in _Game.Elements)
            {
                if (_ElementsMoved.Contains(element))
                {
                    continue;
                }
                if (element.Actions.Count == 0)
                {
                    continue;
                }
                if (element.Player == _PlayerWithTurn.Name)
                {
                    _ElementsReadyToMove.Add(element);
                }
            }

            if (ElementsReadyToMoveChangedEvent != null)
            {
                ElementsReadyToMoveChangedEvent(_ElementsReadyToMove);
            }
        }

        void OnEndOfMove(GameSpace pGame)
        {
            pGame.Elements.Actions.Clear();
        }

        void OnEndOfTurn(GameSpace pGame)
        {
            _TurnValue = true;
            DeterminePlayersTurn();
            DetermineElementsReadyToMove();
        }

        private void OnElementAdded(GameElement element)
        {
            DetermineElementsReadyToMove();
        }

        private void OnElementMoved(GameElement element)
        {
            _ElementsMoved.Add(element);
            DetermineElementsReadyToMove();

            if (_ElementsReadyToMove.Count == 0)
            {
                _ElementsMoved.Clear();

                _Index++;
                DeterminePlayersTurn();
                DetermineElementsReadyToMove();
            }
        }
        
        public FightEventArgs FireFightEvent(
            GameElement attacker, GameElement defender)
        {
            FightEventArgs args = new FightEventArgs();
            args.attacker = attacker;
            args.defender = defender;
            if (Fight != null)
            {
                Fight(args);
            }
            
            return args;
        }

    }
}
